1 module d_snprintf.vararg; 2 3 @nogc: 4 nothrow: 5 6 version (D_TypeInfo) { 7 alias Type = TypeInfo; 8 } else { 9 // Without TypeInfo we use an opaque hash to identify types. 10 alias Type = size_t; 11 } 12 13 struct va_list { 14 void*[] values; 15 Type[] types; 16 size_t current; 17 18 @nogc: 19 nothrow: 20 21 int opApply(int delegate(va_elem) nothrow @nogc dg) { 22 int result = 0; 23 24 for (size_t i = current; i < values.length; i++) { 25 result = dg(va_elem(values[i], types[i])); 26 if (result) { 27 break; 28 } 29 } 30 return result; 31 } 32 } 33 34 struct va_elem { 35 void* value; 36 Type type; 37 } 38 39 mixin template va_start(a...) { 40 ubyte[a.length * (size_t.sizeof) * 2] va_args_buffer; 41 va_list va_args = get_varargs(a, va_args_buffer[]); 42 } 43 pragma(inline, true) void va_end(va_list) {} 44 45 pragma(inline, true) void va_copy(ref va_list copy, ref va_list args) { 46 copy = args; 47 } 48 49 va_list get_varargs(A...)(ref A a, ubyte[] buffer) { 50 va_list result; 51 result.values = (cast(void**)buffer.ptr)[0..a.length]; 52 result.types = (cast(Type*)buffer.ptr)[a.length..a.length * 2]; 53 static foreach (i, t; a) { 54 result.values[i] = cast(void*)&t; 55 result.types[i] = va_get_type!(typeof(t)); 56 } 57 return result; 58 } 59 60 static if (size_t.sizeof == 8) { 61 private enum TYPE_MASK = 0x0FFFFFFFFFFFFFFF; 62 private enum TYPE_POINTER = 0x1000000000000000; 63 private enum TYPE_ARRAY = 0x2000000000000000; 64 private enum TYPE_ENUM = 0x4000000000000000; 65 private enum TYPE_CLASS = 0x8000000000000000; 66 } else { 67 private enum TYPE_MASK = 0x0FFFFFFF; 68 private enum TYPE_POINTER = 0x10000000; 69 private enum TYPE_ARRAY = 0x20000000; 70 private enum TYPE_ENUM = 0x40000000; 71 private enum TYPE_CLASS = 0x80000000; 72 } 73 74 Type va_get_type(T)() { 75 version(D_TypeInfo) { 76 return strip_type_info(typeid(T)); 77 } else { 78 import std.traits : Unqual, isPointer, OriginalType; 79 size_t result = 0; 80 alias stripped_T = Unqual!T; 81 static if (isPointer!(stripped_T)) { 82 alias actual_T = PointerTarget!stripped_T; 83 result |= TYPE_POINTER; 84 } else static if (is(stripped_T : E[], E)) { 85 alias actual_T = E; 86 result |= TYPE_ARRAY; 87 } else static if (is(stripped_T == enum)) { 88 alias actual_T = OriginalType!stripped_T; 89 result |= TYPE_ENUM; 90 } else static if (is(stripped_T == class)) { 91 alias actual_T = stripped_T; 92 result |= TYPE_CLASS; 93 } else { 94 alias actual_T = stripped_T; 95 } 96 97 // We just have to hope this is unique. 98 result |= get_type_hash!(Unqual!actual_T) & TYPE_MASK; 99 return result; 100 } 101 } 102 103 private template get_type_hash(T) { 104 // Removes const, immutabe, shared and inout 105 enum get_type_hash = fnv_1a_hash(T.mangleof); 106 } 107 108 private size_t fnv_1a_hash(string s) { 109 static if (size_t.sizeof == 8) { 110 enum fnv_prime = 0x100000001b3; 111 enum fnv_offset_basis = 0xcbf29ce484222000; 112 } else { 113 enum fnv_prime = 0x1000193; 114 enum fnv_offset_basis = 0x811c9dc5; 115 } 116 size_t hash = fnv_offset_basis; 117 foreach (char value; s) { 118 hash = hash ^ value; 119 hash = hash * fnv_prime; 120 } 121 return hash; 122 } 123 124 pragma(inline, true) T va_arg(T)(ref va_list list) { 125 return *(cast(T*)list.values[list.current++]); 126 } 127 128 pragma(inline, true) T va_peek(T)(ref va_list list) { 129 return *(cast(T*)list.values[list.current]); 130 } 131 132 pragma(inline, true) va_elem va_get_elem(ref va_list list) { 133 return va_elem(list.values[list.current], list.types[list.current]); 134 } 135 136 pragma(inline, true) va_elem va_get_elem(ref va_list list, int i) { 137 return va_elem(list.values[list.current + i], list.types[list.current + i]); 138 } 139 140 pragma(inline, true) T va_value(T)(va_elem elem) { 141 return *(cast(T*)elem.value); 142 } 143 144 pragma(inline, true) size_t va_size(va_list list) { 145 return list.types.length - list.current; 146 } 147 148 pragma(inline, true) Type va_get_type(va_list list) { 149 return list.types[list.current]; 150 } 151 152 pragma(inline, true) bool va_is_enum(Type type) { 153 version(D_TypeInfo) { 154 return (cast(TypeInfo_Enum)type) !is null; 155 } else { 156 return (type & TYPE_ENUM) != 0; 157 } 158 } 159 160 pragma(inline, true) bool va_is_pointer(Type type) { 161 version(D_TypeInfo) { 162 return (cast(TypeInfo_Pointer)type) !is null; 163 } else { 164 return (type & TYPE_POINTER) != 0; 165 } 166 } 167 168 pragma(inline, true) bool va_is_class(Type type) { 169 version(D_TypeInfo) { 170 return (cast(TypeInfo_Class)type) !is null; 171 } else { 172 return (type & TYPE_CLASS) != 0; 173 } 174 } 175 176 pragma(inline, true) bool va_is_array(Type type) { 177 version(D_TypeInfo) { 178 return (cast(TypeInfo_Array)type) !is null; 179 } else { 180 return (type & TYPE_ARRAY) != 0; 181 } 182 } 183 184 pragma(inline, true) Type va_get_enum_base_type(Type type) { 185 if (va_is_enum(type)) { 186 version(D_TypeInfo) { 187 return strip_type_info((cast(TypeInfo_Enum)type).base); 188 } else { 189 return type & TYPE_MASK; 190 } 191 } 192 return type; 193 } 194 195 pragma(inline, true) Type va_get_pointer_target_type(Type type) { 196 if (va_is_pointer(type)) { 197 version(D_TypeInfo) { 198 return strip_type_info((cast(TypeInfo_Pointer)type).m_next); 199 } else { 200 return type & TYPE_MASK; 201 } 202 } 203 return type; 204 } 205 206 pragma(inline, true) Type va_get_array_elem_type(Type type) { 207 assert(va_is_array(type)); 208 version(D_TypeInfo) { 209 return type = strip_type_info((cast(TypeInfo_Array)type).value); 210 } else { 211 return type & TYPE_MASK; 212 } 213 } 214 215 version (D_TypeInfo) { 216 // Removes const, immutabe, shared and inout 217 TypeInfo strip_type_info(TypeInfo type) { 218 219 while ((cast(TypeInfo_Const)type) !is null) { 220 type = (cast(TypeInfo_Const)type).base; 221 } 222 return type; 223 } 224 } 225 226 void* get_any_pointer(ref va_list list) { 227 Type type = va_get_type(list); 228 if (va_is_pointer(type)) { 229 void* result = va_arg!(void*)(list); 230 return result; 231 } else if (va_is_array(type)) { 232 void[] result = va_arg!(void[])(list); 233 return result.ptr; 234 } else if (type is va_get_type!(size_t)) { 235 size_t result = va_arg!(size_t)(list); 236 return cast(void*)result; 237 } else if (va_is_class(type)) { 238 Object result = va_arg!(Object)(list); 239 return cast(void*)result; 240 } else { 241 assert(false, "Tried to read a pointer from varargs but found a different type."); 242 } 243 } 244 245 long get_any_int(ref va_list list) { 246 Type type = va_get_type(list); 247 type = va_get_enum_base_type(type); 248 if (va_get_type!(byte) is type) { 249 return va_arg!byte(list); 250 } else if (va_get_type!(ubyte) is type) { 251 return va_arg!ubyte(list); 252 } else if (va_get_type!(short) is type) { 253 return va_arg!short(list); 254 } else if (va_get_type!(ushort) is type) { 255 return va_arg!ushort(list); 256 } else if (va_get_type!(int) is type) { 257 return va_arg!int(list); 258 } else if (va_get_type!(uint) is type) { 259 return va_arg!uint(list); 260 } else if (va_get_type!(long) is type) { 261 return va_arg!long(list); 262 } else if (va_get_type!(ulong) is type) { 263 return va_arg!ulong(list); 264 } else if (va_get_type!(bool) is type) { 265 return va_arg!bool(list) ? 1 : 0; 266 } else { 267 assert(false, "Tried to read a integer from varargs but found a different type."); 268 } 269 } 270 271 real get_any_float(ref va_list list) { 272 Type type = va_get_type(list); 273 type = va_get_enum_base_type(type); 274 if (va_get_type!(float) is type) { 275 return va_arg!float(list); 276 } else if (va_get_type!(double) is type) { 277 return va_arg!double(list); 278 } else if (va_get_type!(real) is type) { 279 return va_arg!real(list); 280 } else { 281 assert(false, "Tried to read a floating point number from varargs but found a different type."); 282 } 283 } 284 285 // Look for string, char[] or (u)byte[] 286 bool has_string_like_value(ref va_list list) { 287 Type type = va_get_type(list); 288 if (va_get_type!(string) is type) { 289 return true; 290 } else if (va_is_array(type)) { 291 type = va_get_array_elem_type(type); 292 if (va_get_type!(char) is type || va_get_type!(byte) is type || va_get_type!(ubyte) is type) { 293 return true; 294 } 295 } 296 return false; 297 }